// Copyright 2011 The OnTheQuarter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package server

import (
	"appengine"
	"appengine/datastore"
	"appengine/user"
	"fmt"
	"http"
	"log"
	"os"
	"regexp"
	"template"
)

var htmlSet = template.SetMust(template.ParseTemplateGlob("html/*.html"))

func init() {
	http.HandleFunc("/", root)
	http.HandleFunc("/splash", splash)
	http.HandleFunc("/demo", demo)
}

func splash(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	url, err := user.LoginURL(c, fullUrl(r))
	if err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	if err := htmlSet.Execute(w, "splash.html", url); err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
	}
}

func demo(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	url, err := user.LoginURL(c, fullUrl(r))
	if err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	var data struct {
		Slider    Slider
		SignupURL string
	}
	data.Slider = makeSlider(currentQuarter())
	data.SignupURL = url
	if err := htmlSet.Execute(w, "demo.html", data); err != nil {
		http.Error(w, err.String(), http.StatusInternalServerError)
	}
}

type SliderQuarter struct {
	Quarter  Quarter
	Selected bool
	ShowYear bool
}

type Slider struct {
	Quarter []SliderQuarter
}

func makeSlider(qtr Quarter) Slider {
	// TODO: actually support sliding quarters.
	return Slider{
		Quarter: []SliderQuarter{
			SliderQuarter{
				Quarter: "2012q3",
				Selected: qtr == "2012q3",
				ShowYear: false,
			},
			SliderQuarter{
				Quarter: "2012q2",
				Selected: qtr == "2012q2",
				ShowYear: false,
			},
			SliderQuarter{
				Quarter: "2012q1",
				Selected: qtr == "2012q1",
				ShowYear: true,
			},
			SliderQuarter{
				Quarter: "2011q4",
				Selected: qtr == "2011q4",
				ShowYear: true,
			},
		},
	}
}

type Root struct {
	Objective  []Objective
	Slider     Slider
	CompiledJS bool
}

var defaultObjectives = []Objective{
	Objective{
		Text: "Be a good person",
		Result: []Result{
			Result{
				Text: "Complement someone every day.",
			},
		},
	},
}

func (o *OKRs) unpack() []Objective {
	obj := []Objective{}
	for i, text := range o.Text {
		if o.IsChild[i] {
			last := &obj[len(obj)-1]
			last.Result = append(last.Result, Result{
				Text: text,
				Score: o.Score[i],
			})
		} else {
			obj = append(obj, Objective{
				Text: text,
				Score: o.Score[i],
				Result: []Result{},
			})
		}
	}
	return obj
}

func packOKRs(obj []Objective) *OKRs {
	okrs := new(OKRs)
	for _, o := range obj {
		okrs.Text = append(okrs.Text, o.Text)
		okrs.Score = append(okrs.Score, o.Score)
		okrs.IsChild = append(okrs.IsChild, false)
		for _, r := range o.Result {
			okrs.Text = append(okrs.Text, r.Text)
			okrs.Score = append(okrs.Score, r.Score)
			okrs.IsChild = append(okrs.IsChild, true)
		}
	}
	return okrs
}

func (r *Root) load(c appengine.Context, name string, qtr Quarter) os.Error {
	pkey := datastore.NewKey(c, "OTQ", name, 0, nil)
	qkey := datastore.NewKey(c, "OTQ", string(qtr), 0, pkey)

	okrs := new(OKRs)
	err := datastore.Get(c, qkey, okrs)
	if err == datastore.ErrNoSuchEntity {
		r.Objective = defaultObjectives
		return nil
	}
	if err != nil {
		return err
	}

	r.Objective = okrs.unpack()
	return nil
}

var rootPath = regexp.MustCompile(`^/([^/]+)/([0-9]+q[1-4])`)

// TODO: name is misleading.
func fullUrl(r *http.Request) string {
	src := "http"
	if r.TLS != nil {
		src = src + "s"
	}
	src = src + "://" + r.Host + "/upgrade"
	return src
}

func root(w http.ResponseWriter, r *http.Request) {
	// Load current user. p is nil if not a user.
	c := appengine.NewContext(r)
	u := user.Current(c)
	var p *Person
	if u != nil {
		k := datastore.NewKey(c, "Person", u.Id, 0, nil)
		p = new(Person)
		if err := datastore.Get(c, k, p); err != nil {
			if err == datastore.ErrNoSuchEntity {
				p = nil
			} else {
				http.Error(w, err.String(), http.StatusInternalServerError)
				return
			}
		}
	}

	if u != nil && p == nil {
		http.Redirect(w, r, "/upgrade", http.StatusTemporaryRedirect)
		return
	}

	if r.URL.Path == "/" {
		if p == nil {
			splash(w, r)
			return
		} else {
			qtr := currentQuarter()
			url := fmt.Sprintf("/%s/%s", p.ShortName, qtr)
			http.Redirect(w, r, url, http.StatusTemporaryRedirect)
		}
		return
	}

	// expect path of the form: /shortname/YYYYq[1-4]
	s := rootPath.FindStringSubmatch(r.URL.Path)
	if s == nil {
		err := fmt.Errorf("unexpected path: %s", r.URL.Path)
		log.Print(err)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
	shortname := s[1]
	qtr := Quarter(s[2])
	log.Printf("shortname %s, quarter %s", shortname, qtr)

	root := Root{}
	root.CompiledJS = !appengine.IsDevAppServer()
	root.Slider = makeSlider(qtr)
	if err := root.load(c, shortname, qtr); err != nil {
		log.Print(err)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}

	if err := htmlSet.Execute(w, "root.html", &root); err != nil {
		log.Print(err)
		http.Error(w, err.String(), http.StatusInternalServerError)
		return
	}
}
